import pandas as pd
import requests
from datetime import datetime
import re
from pathlib import Path
import time


def extract_repo_info(url):
    """从GitHub或Gitee链接中提取仓库信息"""
    clean_url = url.split('?')[0].split('#')[0]
    github_pattern = r'https://github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$'
    gitee_pattern = r'https://gitee\.com/([^/]+)/([^/]+?)(?:\.git)?/?$'

    github_match = re.search(github_pattern, clean_url)
    gitee_match = re.search(gitee_pattern, clean_url)

    if github_match:
        return {'platform': 'github', 'username': github_match.group(1), 'repo': github_match.group(2)}
    elif gitee_match:
        return {'platform': 'gitee', 'username': gitee_match.group(1), 'repo': gitee_match.group(2)}
    else:
        return None


def check_repo_existence(platform, username, repo, token=None):
    """
    预检查仓库是否存在（新增函数）
    返回：(bool, str) → (仓库是否存在, 错误信息/空字符串)
    """
    headers = {'Accept': 'application/vnd.github.v3+json'} if platform == 'github' else {}
    params = {'access_token': token} if (platform == 'gitee' and token) else {}
    
    # 构造仓库基本信息接口URL（非提交接口，轻量级检查）
    if platform == 'github':
        url = f'https://api.github.com/repos/{username}/{repo}'
        if token:
            headers['Authorization'] = f'token {token}'
    elif platform == 'gitee':
        url = f'https://gitee.com/api/v5/repos/{username}/{repo}'
    else:
        return False, "不支持的平台"

    try:
        response = requests.get(url, headers=headers, params=params)
        if response.status_code == 200:
            return True, ""  # 仓库存在
        elif response.status_code == 404:
            return False, "仓库不存在"
        elif response.status_code == 403:
            return False, "API访问受限（可能需要令牌）"
        elif response.status_code == 401:
            return False, "认证失败（令牌无效）"
        else:
            return False, f"未知错误（状态码：{response.status_code}）"
    except requests.exceptions.RequestException as e:
        return False, f"网络错误：{str(e)[:50]}"  # 截取短错误信息


def get_github_commits(username, repo, token=None):
    """获取GitHub仓库的提交历史（保持原逻辑，新增分支参数兼容）"""
    # 新增：支持指定分支（默认取仓库默认分支，避免分支不存在导致的409错误）
    url = f'https://api.github.com/repos/{username}/{repo}/commits'
    headers = {'Accept': 'application/vnd.github.v3+json'}
    if token:
        headers['Authorization'] = f'token {token}'

    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 403:
            return [], "GitHub API访问受限，请稍后再试或配置访问令牌"
        elif response.status_code == 404:
            return [], "GitHub仓库不存在或无访问权限"
        elif response.status_code == 401:
            return [], "GitHub认证失败，请检查访问令牌"
        elif response.status_code == 409:
            return [], "GitHub仓库分支冲突（可能默认分支不存在）"
        response.raise_for_status()
        
        commits = response.json()
        commit_data = [{
            'sha': c['sha'],
            'message': c['commit']['message'],
            'author': c['commit']['author']['name'],
            'date': c['commit']['author']['date']
        } for c in commits]
        return commit_data, ""
    except requests.exceptions.RequestException as e:
        return [], f"获取GitHub提交历史出错：{str(e)[:50]}"


def get_gitee_commits(username, repo, token=None):
    """获取Gitee仓库的提交历史（返回错误信息，便于日志记录）"""
    url = f'https://gitee.com/api/v5/repos/{username}/{repo}/commits'
    params = {'access_token': token} if token else {}

    try:
        response = requests.get(url, params=params)
        if response.status_code == 403:
            return [], "Gitee API访问受限，请稍后再试或配置访问令牌"
        elif response.status_code == 404:
            return [], "Gitee仓库不存在或无访问权限"
        elif response.status_code == 401:
            return [], "Gitee认证失败，请检查访问令牌"
        response.raise_for_status()
        
        commits = response.json()
        commit_data = [{
            'sha': c['sha'],
            'message': c['commit']['message'],
            'author': c['commit']['author']['name'],
            'date': c['commit']['author']['date']
        } for c in commits]
        return commit_data, ""
    except requests.exceptions.RequestException as e:
        return [], f"获取Gitee提交历史出错：{str(e)[:50]}"


def read_excel_file(file_path):
    """读取Excel文件（保持原逻辑）"""
    try:
        df = pd.read_excel(file_path)
        repos = []
        for index, row in df.iterrows():
            url = str(row['gitee/github链接']).strip() if pd.notna(row['gitee/github链接']) else ''
            if not url:
                continue
            repo_info = extract_repo_info(url)
            if repo_info:
                repo_info.update({
                    'name': str(row['姓名']) if pd.notna(row['姓名']) else '',
                    'student_id': str(row['学号']) if pd.notna(row['学号']) else '',
                    'class': str(row['班级']) if pd.notna(row['班级']) else '',
                    'original_url': url,
                    'error_msg': ''  # 新增：存储仓库处理过程中的错误信息
                })
                repos.append(repo_info)
            else:
                print(f"无法解析链接: {url}")
        return repos
    except Exception as e:
        print(f"读取Excel文件时出错: {e}")
        return []


def parse_date(date_str):
    """解析日期（保持原逻辑）"""
    try:
        if date_str.endswith('Z'):
            return datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ")
        elif '+' in date_str or date_str.count('-') > 2:
            return datetime.fromisoformat(date_str)
        else:
            return datetime.fromisoformat(date_str)
    except Exception as e:
        return None


def write_commit_report(commits_data, output_file):
    """
    写入提交报告（新增「错误记录」章节，优化统计逻辑）
    commits_data：包含每个仓库的提交数据+错误信息
    """
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write("# 仓库提交情况统计报告\n\n")
        f.write(f"报告生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")  # 新增生成时间
        
        total_commits = 0
        student_stats = {}
        error_records = []  # 存储所有错误记录，用于后续章节
        
        # 1. 逐个处理学生仓库信息
        for repo_data in commits_data:
            f.write(f"## 学生: {repo_data['name']}（学号: {repo_data['student_id']}）\n")
            f.write(f"班级: {repo_data['class']}\n")
            f.write(f"仓库链接: {repo_data['original_url']}\n\n")

            # 若有错误信息，优先展示并记录
            if repo_data['error_msg']:
                f.write(f"⚠️ **处理错误**: {repo_data['error_msg']}\n\n")
                error_records.append({
                    'student': repo_data['name'],
                    'student_id': repo_data['student_id'],
                    'url': repo_data['original_url'],
                    'error': repo_data['error_msg']
                })
                student_stats[repo_data['name']] = 0
                continue

            # 无错误但无提交记录
            if not repo_data['commits']:
                f.write("ℹ️ 未找到提交记录（仓库存在但无提交）\n\n")
                student_stats[repo_data['name']] = 0
                continue

            # 展示提交表格
            f.write("| 提交SHA（前8位） | 作者 | 提交时间 | 提交信息（前50字） |\n")
            f.write("|------------------|------|----------|--------------------|\n")
            for commit in repo_data['commits']:
                total_commits += 1
                date_obj = parse_date(commit['date'])
                formatted_date = date_obj.strftime("%Y-%m-%d %H:%M:%S") if date_obj else "日期解析失败"
                short_msg = commit['message'][:50] + "..." if len(commit['message']) > 50 else commit['message']
                f.write(f"| {commit['sha'][:8]} | {commit['author']} | {formatted_date} | {short_msg} |\n")

            # 单个仓库统计
            f.write(f"\n### 仓库统计\n")
            f.write(f"- 总提交次数: {len(repo_data['commits'])}\n")
            authors = set(c['author'] for c in repo_data['commits'])
            f.write(f"- 提交作者: {', '.join(authors) if authors else '未知'}\n\n")
            student_stats[repo_data['name']] = len(repo_data['commits'])

        # 2. 新增「错误记录汇总」章节
        if error_records:
            f.write("# 错误记录汇总\n\n")
            f.write("| 学生 | 学号 | 仓库链接 | 错误原因 |\n")
            f.write("|------|------|----------|----------|\n")
            for err in error_records:
                f.write(f"| {err['student']} | {err['student_id']} | {err['url']} | {err['error']} |\n")
            f.write("\n")

        # 3. 总体统计
        f.write("# 总体统计信息\n\n")
        f.write(f"- 处理的唯一学生仓库数: {len(commits_data)}\n")
        f.write(f"- 所有仓库总提交次数: {total_commits}\n")
        f.write(f"- 存在错误的仓库数: {len(error_records)}\n\n")

        # 按提交次数排序的学生统计
        f.write("### 学生提交次数排名\n")
        f.write("| 排名 | 学生 | 提交次数 |\n")
        f.write("|------|------|----------|\n")
        # 排序：先按提交次数降序，再按学生姓名升序
        sorted_stats = sorted(student_stats.items(), key=lambda x: (-x[1], x[0]))
        for rank, (student, count) in enumerate(sorted_stats, 1):
            f.write(f"| {rank} | {student} | {count} |\n")


def main():
    # 配置部分
    excel_file = 'E:\python test\pachong\students2.xlsx'
    output_file = '软工2班提交报告.md'
    github_token = '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'  # 需配置时替换为实际令牌
    gitee_token = 'b4409362c92f71477ee3d866048741da'
    api_delay = 3  # 优化：延迟延长至3秒，减少限流风险（处理50+仓库建议用3-5秒）

    # 检查Excel文件
    if not Path(excel_file).exists():
        print(f"错误: 找不到Excel文件 '{excel_file}'")
        return

    # 读取仓库信息并去重
    repos = read_excel_file(excel_file)
    if not repos:
        print("未找到有效的仓库链接")
        return

    # 去重：同一学生（姓名+学号）只处理一次
    processed_students = set()
    unique_repos = []
    for repo in repos:
        student_key = (repo['name'], repo['student_id'])
        if student_key not in processed_students:
            processed_students.add(student_key)
            unique_repos.append(repo)

    print(f"共发现 {len(unique_repos)} 个唯一学生仓库，开始处理...\n")
    all_commits_data = []

    # 逐个处理仓库（新增预检查+错误捕获）
    for i, repo in enumerate(unique_repos, 1):
        print(f"[{i}/{len(unique_repos)}] 处理 {repo['name']}（{repo['platform']}）...")
        repo_copy = repo.copy()
        repo_copy['commits'] = []  # 初始化提交列表

        # 1. 预检查仓库是否存在
        exists, check_err = check_repo_existence(
            platform=repo['platform'],
            username=repo['username'],
            repo=repo['repo'],
            token=github_token if repo['platform'] == 'github' else gitee_token
        )
        if not exists:
            repo_copy['error_msg'] = check_err
            all_commits_data.append(repo_copy)
            print(f"  ❌ 预检查失败：{check_err}")
            time.sleep(api_delay)
            continue

        # 2. 预检查通过，获取提交历史
        if repo['platform'] == 'github':
            commits, commit_err = get_github_commits(repo['username'], repo['repo'], github_token)
        else:
            commits, commit_err = get_gitee_commits(repo['username'], repo['repo'], gitee_token)

        # 3. 处理提交结果
        if commit_err:
            repo_copy['error_msg'] = commit_err
            print(f"  ❌ 获取提交失败：{commit_err}")
        else:
            repo_copy['commits'] = commits
            print(f"  ✅ 成功获取 {len(commits)} 条提交记录")

        all_commits_data.append(repo_copy)
        # 延迟：最后一个仓库无需延迟
        if i < len(unique_repos):
            time.sleep(api_delay)

    # 生成报告
    write_commit_report(all_commits_data, output_file)
    print(f"\n✅ 报告已生成：{output_file}")
    print(f"⚠️  共 {len([d for d in all_commits_data if d['error_msg']])} 个仓库处理出错（详见报告）")


if __name__ == "__main__":
    main()
